#include "../include/soundplayer.h"
#include <alsa/asoundlib.h>
#include <linux/soundcard.h>
#include <sys/ioctl.h>
#include <stdint.h>

environment_t env;

int pcm_output_bytes_to_frames(int bytes){
	//convert bytes to frames
	int frames = bytes/env->channels/(env->bit_depth/8);
	return frames;
}

int pcm_output_frames_to_bytes(int frames){
	//convert frames to bytes
	int bytes = frames*env->channels*(env->bit_depth/8);
	return bytes;
}

int pcm_output_init(){
	//initialize the given sound system
	static snd_pcm_t* pcm;
	static FILE* dsp;
	int retval = 0;
	char header[44];

	switch (env->audio_out){
	case 0:
		//initialize advanced linux sound architecture
		env->message("using ALSA output module\n");
		retval = snd_pcm_open(&pcm, env->audio_device,  SND_PCM_STREAM_PLAYBACK, 0);
		env->pcm_handle = pcm;
		break;
	case 1:
		//initialize the open sound system
		env->message("using OSS output module\n");

		if ((dsp = fopen("/dev/dsp", "w")) == NULL){
			env->message("Failed to open /dev/dsp\n");
			retval = 1;
		}
		env->pcm_handle = dsp;
		break;
	default:
		//assume that output is a file
		env->message("Outputting to %s\n", env->filename);
		if ((dsp = fopen(env->filename, "w")) == NULL){
			env->message("Failed to open file.\n");
			retval = 1;
		}
		else {
			fwrite(header, 1, 44, dsp); //pad bytes for wav headers
		}
		env->pcm_handle = dsp;
	}

	pcm_output_configure();

	return retval;
}

int pcm_output_configure(){
	switch(env->audio_out){
		case 0:
			if(env->rate == 24000) {
         env->rate++; //DUMBFIX: please replace
         pcm_output_configure();
      }
			snd_pcm_set_params(env->pcm_handle, env->bit_depth_alsa, SND_PCM_ACCESS_RW_INTERLEAVED, env->channels, env->rate, 1, 500000 /*500ms latency*/);
		case 1:
			ioctl(fileno(env->pcm_handle), SOUND_PCM_WRITE_BITS, &env->bit_depth);                     //set bit depth
			ioctl(fileno(env->pcm_handle), SOUND_PCM_WRITE_CHANNELS, &env->channels);                     //set audio channels
			ioctl(fileno(env->pcm_handle), SOUND_PCM_WRITE_RATE, &env->rate);                     //set sample rate
	}
	return 1;
}

int pcm_output_write(void* buffer, int buffer_size){
	//handle the buffer passed from the decoder
	//underrun and other errors handled by passing > 0 as return value
	//buffer_size is the number of samples its up to the individual handlers to deal with conversion
	int retval = 0;
	int rc;
	char* b = (char*) buffer; //default to passing unmodified input
	if (env->overrides&SP_OVERRIDE_FILTER) b = env->filter(buffer, buffer_size);
	switch (env->audio_out){
	case 0:
		rc = snd_pcm_writei(env->pcm_handle, b, buffer_size);
		if (rc != -EPIPE){
			break;
		}
		else {
			snd_pcm_prepare(env->pcm_handle);
			env->message("buffer underrun\n");
			retval = 1;
			break;
		}
	case 1:
		rc = fwrite(b, 1, pcm_output_frames_to_bytes(buffer_size), env->pcm_handle);
		break;
	default:
		rc = fwrite(b, 1, pcm_output_frames_to_bytes(buffer_size), env->pcm_handle);
	}
	return retval;
}

int pcm_output_term(){
	//deinitialize the sound system and clean up
	uint16_t x;
	uint32_t y;
	uint32_t total_size;

	switch (env->audio_out){
	case 0:
		//closing alsa pcm handle
		snd_pcm_drain(env->pcm_handle);
		snd_pcm_close(env->pcm_handle);
		break;
	case 1:
		//closing dsp device
		fclose(env->pcm_handle);
		break;
	default:
		fseek(env->pcm_handle, 0, SEEK_END);
		total_size = (ftell(env->pcm_handle)-44);
		y = total_size+36;
		rewind(env->pcm_handle);
		fwrite("RIFF", 1, 4, env->pcm_handle); //magic number
		fwrite(&y, sizeof(uint32_t), 1, env->pcm_handle); //write size field
		fwrite("WAVEfmt ", 1, 8, env->pcm_handle); //this WAVEfmt thing
		y = 16; fwrite(&y, sizeof(uint32_t), 1, env->pcm_handle); //the number 16
		x = 1; fwrite(&x, sizeof(uint16_t), 1, env->pcm_handle); //write the number 1
		x = env->channels; fwrite(&x, sizeof(uint16_t), 1, env->pcm_handle); //write number of channels
		y = env->rate; fwrite(&y, sizeof(uint32_t), 1, env->pcm_handle); //samplerate
		y *= env->channels*(env->bit_depth/8); fwrite(&y, sizeof(uint32_t), 1, env->pcm_handle);   //this field
		x = env->channels*(env->bit_depth/8); fwrite(&x, sizeof(uint16_t), 1, env->pcm_handle);   //that field
		x = env->bit_depth; fwrite(&x, sizeof(uint16_t), 1, env->pcm_handle); //bits per sample field
		fwrite("data", 1, 4, env->pcm_handle);
		fwrite(&total_size, sizeof(uint32_t), 1, env->pcm_handle);

		fclose(env->pcm_handle);
	}
	return 0; //dummy return
}
